package edu.bath.sensorframework.sensors;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.jivesoftware.smack.XMPPException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import edu.bath.sensorframework.DataReading;

public class VirtualSensor extends edu.bath.sensorframework.sensor.Sensor  {
	
	public VirtualSensor(String serverAddress, String serverName, String id,
			String password, String nodeName) throws XMPPException {
		super(serverAddress, serverName, id, password, nodeName);
	}
	
	SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
	
	public static void main(String[] args) throws Exception {
		if(args.length != 1)
			throw new IllegalArgumentException("Didn't pass a virtual sensor descriptor.");
		
		File descriptor = new File(args[0]);
		if(!descriptor.exists())
			throw new IllegalArgumentException("Virtual sensor descriptor '"+args[0]+"' cannot be opened!");
		
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
		Document doc = docBuilder.parse(descriptor);
		Element rootNode = doc.getDocumentElement();
		
		// Read out base directory, and make sure it can be used directly as a file prefix
		String baseDirectory = rootNode.getAttribute("basedir");
		if(!baseDirectory.endsWith("/"))
			baseDirectory = baseDirectory+"/";
		System.out.println("Base directory: "+baseDirectory);
		
		List<Group> groupList = new ArrayList<Group>();
		// Parse XML document
		NodeList groups  = rootNode.getChildNodes();
		for(int g = 0 ; g < groups.getLength() ; g++) {
			Node group = groups.item(g);
			if(group.getNodeType() != Node.ELEMENT_NODE)
				continue;
			Group grp = new Group();
			
			// Fetch the group name
			NamedNodeMap attributes = group.getAttributes();
			Node name = attributes.getNamedItem("name");
			grp.name = name.getNodeValue();
			System.out.println(grp.name);
			groupList.add(grp);
			
			// Fetch subgroups
			NodeList subgroups = group.getChildNodes();
			for(int sg = 0 ; sg < subgroups.getLength() ; sg++) {
				Node subgroup = subgroups.item(sg);
				if(subgroup.getNodeType() != Node.ELEMENT_NODE)
					continue;
				SubGroup subgrp = new SubGroup();
				grp.subgroups.add(subgrp);
				
				// Fetch the subgroup name
				attributes = subgroup.getAttributes();
				name = attributes.getNamedItem("name");
				subgrp.name = name.getNodeValue();
				System.out.println("   "+subgrp.name);
				
				// Fetch sensors
				NodeList sensors = subgroup.getChildNodes();
				for(int s = 0 ; s < sensors.getLength() ; s++) {
					Node sensor = sensors.item(s);
					if(sensor.getNodeType() != Node.ELEMENT_NODE)
						continue;
					Sensor sens = new Sensor();
					subgrp.sensors.add(sens);
					
					// Fetch location and address
					attributes = sensor.getAttributes();
					sens.location = attributes.getNamedItem("location").getNodeValue();
					sens.filename = baseDirectory+add2Fn(attributes.getNamedItem("address").getNodeValue());
					sens.id = attributes.getNamedItem("about").getNodeValue();
					sens.type = attributes.getNamedItem("type").getNodeValue();
					sens.dataType = attributes.getNamedItem("data").getNodeValue();
					sens.file = new File(sens.filename);
					sens.name = sensor.getChildNodes().item(0).getNodeValue();
					
					System.out.println("      "+sens.location);
					System.out.println("      "+sens.filename);
					System.out.println("      "+sens.name+"\n");
					
					sens.load();
				}
			}
		}
		
		VirtualSensor vs = new VirtualSensor("laptop", "laptop", "smarthouse", "bobbo", "smartHouse");
		vs.run(groupList, "15.01.2009 18:17:00"); // TODO: Correct this!
	}
	
	public void run(List<Group> groupList, String startTime) throws ParseException {
		// Initialise virtual date
		VirtualTime vt = new VirtualTime(startTime);
		for(Group g : groupList) {
			g.init(vt.virtualTimeMillis());
		}
		
		while(true) {
			Date d = new Date(vt.virtualTimeMillis());
			System.out.println("Simulated time is.. "+d.toString());
			try { Thread.sleep(500); } catch (InterruptedException e) {}
			
			for(Group g : groupList) {
				for(SubGroup sg : g.subgroups) {
					for(Sensor s : sg.sensors) {
						SensorReading sr = s.getNextReading(vt.virtualTimeMillis());
						if(sr != null) {
							DataReading dr = new DataReading(s.id, s.location, sr.ts);
							dr.setType(s.type);
							// TODO: need to make this far better, so we can actually deal with different types of data coming out of a sensor not just boolean
							// if we looked up s.data, we'd find that it gives out onOff data, which we know is in turn boolean. this will require actually looking up semantics now
							dr.addDataValue(null, "http://bath.edu/sensors/types#onOff", (sr.data.equals("1")?true:false), false);
							
							// TODO: Handle this properly
							System.out.println("Sensor triggered!");
							System.out.println("    "+s.location);
							System.out.println("    "+s.name);
							System.out.println("        "+sr.data+"\n");
							
							try {
								publish(dr);
							} catch (UnsupportedEncodingException e1) {
								// TODO Auto-generated catch block
								e1.printStackTrace();
							}
							
							try {
								System.out.println("Got RDF data: "+dr.toRDF());
							} catch (UnsupportedEncodingException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
	}

	// Address to filename
	public static String add2Fn(String addr) {
		return addr.replace('/', '_')+".csv";
	}
}
